package interpreter;

import interpreter.bytecodes.*;
import java.util.*;

/**
 * The Program class will hold the byte code program once it's loaded from
 * file
 * 
 * @author Kristofer Selbekk
 */
public class Program {

    private Vector<ByteCode> program; // The program (its byte code objects) are stored here
    private String path;

    /**
     * CONSTRUCTOR:
     * Instantiates the program-vector
     */
    public Program() {
        program = new Vector<ByteCode>();
    }

    /**
     * Adds ByteCode object to Program-vector
     * @param c ByteCode-object to be added
     */
    public void addCode( ByteCode c ) {
        program.add( c );
    }

    /**
     * Gets ByteCode object @ index <code>codeIndex</code> from Program-vector
     *
     * @param codeIndex The index the ByteCode is stored in
     * @return The ByteCode at index <code>codeIndex</code>
     */
    public ByteCode getCode(int codeIndex) {
        return program.get(codeIndex);
    }

    /**
     * Resolves all symbolic addresses and pointers in the completely loaded program
     */
    public void resolveAddr() {
        // Saves the lines to resolve here
        HashMap labels = new HashMap();

        // First iteration -> Saves the labels in temporary data structure
        for( int i = 0; i < program.size(); i++ ) {
            if( program.get(i) instanceof LabelByteCode ) {
                labels.put(program.get(i).getArgs(), i);
            }
        }

        // Second iteration -> Resolves byte codes that needs address resolution
        for( int i = 0; i < program.size(); i++ ) {

            if( program.get(i) instanceof CallByteCode ) {
                // If the address is found among the labels
                if( labels.containsKey( program.get(i).getArgs() ) ) {
                    int addr = (Integer) labels.get( program.get(i).getArgs() );
                    ((CallByteCode) program.get(i)).setAddress( addr );
                } 
            } else if( program.get(i) instanceof FalseBranchByteCode ) {
                // If the address is found among the labels
                if( labels.containsKey( program.get(i).getArgs() ) ) {
                    int addr = (Integer) labels.get( program.get(i).getArgs() );
                    ((FalseBranchByteCode) program.get(i)).setAddress( addr );
                } 
            } else if( program.get(i) instanceof GoToByteCode ) {
                // If the address is found among the labels
                if( labels.containsKey( program.get(i).getArgs() ) ) {
                    int addr = (Integer) labels.get( program.get(i).getArgs() );
                    ((GoToByteCode) program.get(i)).setAddress( addr );
                } 
            } else if( program.get(i) instanceof ReturnByteCode ) {
                // If the address is found among the labels
                if( labels.containsKey( program.get(i).getArgs() ) ) {
                    int addr = (Integer) labels.get( program.get(i).getArgs() );
                    ((ReturnByteCode) program.get(i)).setAddress( addr );
                }
            }
        } // End for
    } // End resolveAddr()

    /*
     * Setter and getters for the file path
     */
    public void setPath(String p) { path = p; }
    public String getPath() { return path; }

    /**
     * Prints out the byte codes
     *
     * <strong>For debugging purposes only</strong>
     *
     * @return a representation of the program
     */
    public String toString() {
        String out = "";
        Iterator it = program.iterator();

        while( it.hasNext() )
            out += it.next() + "\n";
        
        return out;
    }
}
